<template>
  <div style="width: 100%; margin: 0 auto" ref="allPage" @scroll="scrollPage">
    <canvas v-for="index in numPages" :key="index" :ref="`scrollPdfCanvas${index}`" class="pdf-preview"></canvas>
  </div>
</template>
<script lang="ts">
import { defineComponent, nextTick, getCurrentInstance, watch, onMounted, toRefs, reactive } from 'vue'
// eslint-disable-next-line no-restricted-imports
import { throttle } from 'lodash'
// https://github.com/mozilla/pdf.js/issues/13190
import * as pdfjsLib from 'pdfjs-dist/legacy/build/pdf.js'
import PDFJSWorker from 'pdfjs-dist/build/pdf.worker.entry'
// https://github.com/mozilla/pdf.js/issues/11990
pdfjsLib.GlobalWorkerOptions.workerSrc = PDFJSWorker

export default defineComponent({
  name: 'PreviewPdf',
  props: {
    url: {
      type: String,
      default: ''
    },
    currentPage: {
      type: Number,
      default: 1
    },
    rotate: {
      type: Number,
      default: 0
    }
  },
  emits: ['onScrollPage', 'onLoadSuccess', 'onAllPpages'],
  setup(props, { emit }) {
    const instance: any = getCurrentInstance()
    const state = reactive({
      numPages: 0, // pdf 总页数
      isChangeScroll: false,
      isChangePage: false,
      pdfData: null as any
    })

    // 页面滚动时计算当前在第几页
    const scrollPage = throttle((e) => {
      changePage(e.target)
    }, 200)

    const changePage = (e: any) => {
      // 控制currentPage触发滚动时 不走下面逻辑
      if (state.isChangePage) {
        state.isChangePage = false
        return
      }
      const pdfHeight = e.children[0].clientHeight
      const scrollTop = e.scrollTop + e.offsetHeight
      let page = Math.ceil(scrollTop / pdfHeight)
      if (page > state.numPages) {
        page = state.numPages
      }
      // 当前页滚动时 不会触发currentPage变化
      if (page !== props.currentPage) {
        state.isChangeScroll = true
      }
      emit('onScrollPage', page)
    }

    const renderScrollPdfPage = (num: any, rotate = 0) => {
      state.pdfData.promise.then((pdf: any) => {
        const numPages = pdf.numPages
        // 用于获取PDF文档中的各个页面
        pdf.getPage(num).then((page: any) => {
          const canvas = instance?.refs[`scrollPdfCanvas${num}`][0]
          const scale = 2 // 解决字体模糊问题

          // 针对提供的展示比例，返回PDf文档的页面尺寸 getViewport({ scale, rotation, dontFlip })
          const viewport = page.getViewport({ scale, rotate })
          const { width, height } = viewport
          canvas.height = height
          canvas.width = width

          const ctx = canvas.getContext('2d')
          const renderContext = {
            canvasContext: ctx,
            viewport
          }
          // 渲染PDF
          page.render(renderContext).promise.then(() => {
            if (num < numPages) {
              renderScrollPdfPage(num + 1, rotate)
            }
          })
        })
      })
    }

    const renderScrollPdf = () => {
      state.pdfData.promise.then((pdf: any) => {
        state.numPages = pdf.numPages
        emit('onAllPpages', state.numPages)
        renderScrollPdfPage(1)
        nextTick(() => {
          emit('onLoadSuccess', false)
        })
      })
    }

    const loadPdfData = () => {
      emit('onLoadSuccess', true)
      // 用于异步获取PDf文档，发送多个Ajax请求以块的形式下载文档。它返回一个Promise，该Promise的成功回调传递一个对象，该对象包含PDF文档的信息，该回调中的代码将在完成PDf文档获取时执行。
      state.pdfData = pdfjsLib.getDocument(props.url)
      renderScrollPdf()
    }

    watch(
      () => props.currentPage,
      (val: number) => {
        // 控制通过鼠标滚动触发时 不走下面逻辑
        if (state.isChangeScroll) {
          state.isChangeScroll = false
          return
        }
        // currentPage变化时 重置滚动条
        state.isChangePage = true
        const allPage = instance?.refs.allPage as any
        const pdfHeight = allPage.children[0].clientHeight
        allPage.scrollTop = pdfHeight * (val - 1)
      }
    )

    watch(
      () => props.url,
      () => {
        loadPdfData()
      }
    )

    watch(
      () => props.rotate,
      (val: number) => {
        renderScrollPdfPage(1, val)
      }
    )

    onMounted(() => {
      loadPdfData()
    })

    return {
      scrollPage,
      ...toRefs(state)
    }
  }
})
</script>
<style lang="stylus" scoped>
.pdf-preview {
  padding 8px
  width 100%
}
</style>
